<!doctype html>
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<style>
        html,body{
            width: 100%;
            height: 100%;
        }
		body{margin: 0;padding:0;-moz-user-select: none;}
        .view{
            position: relative;
            width: 100%;
            height: 100%;
            overflow: hidden;
        }
		.tips{
            position: relative;
            text-align: center;
            z-index: 999;
        }
		#cas{
            position: absolute;
            display: block;
            top: 0;
            left: 0;
            right: 0;
            margin: auto;
            cursor: move;
        }
	</style>
	<title>3d多立方体</title>
</head>
<body onselectstart="return false">
<div class="view">
    <p class="tips">鼠标拖动立方体转动，滚动鼠标滚轮缩放~</p>
    <canvas id="cas" width="700" height="600">您的浏览器不支持canvas</canvas>
</div>

<script src="../public/stats.min.js"></script>
<script>
	'use strict';

	var stats = new Stats();
	stats.setMode(0);
	stats.domElement.style.position = 'absolute';
	stats.domElement.style.right = '0px';
	stats.domElement.style.top = '0px';
	document.body.appendChild( stats.domElement );

	var canvas = document.getElementById("cas");

    canvas.width = document.body.offsetWidth;
    canvas.height = document.body.offsetHeight;

	var ctx = canvas.getContext("2d");
	var fallLength = 1000 ,
		centerX = canvas.width/2 ,
		centerY = canvas.height/2 ,
		cubeWidth = 100;

	window.RAF = (function(){
		return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {window.setTimeout(callback, 1000 / 60); };
	})();

	var Cube = function(opt){
		//存放所有点坐标
		this.dots = [];

		//存放面
		this.faces = [];

		this.init(opt);
	};

	Cube.prototype = {
		init:function(opt){
			this.xpos = opt.x;
			this.ypos = opt.y;
			this.zpos = opt.z;
            this.w = opt.w;

			for(var i=0;i<8;i++){
				this.dots.push({x:0,y:0,z:0})
			}

			this.setVector();

			this.faces.push([this.dots[0] , this.dots[1] , this.dots[3] , this.dots[2]]);
			this.faces.push([this.dots[2] , this.dots[3] , this.dots[5] , this.dots[4]]);
			this.faces.push([this.dots[4] , this.dots[5] , this.dots[7] , this.dots[6]]);
			this.faces.push([this.dots[6] , this.dots[7] , this.dots[1] , this.dots[0]]);
			this.faces.push([this.dots[1] , this.dots[3] , this.dots[5] , this.dots[7]]);
			this.faces.push([this.dots[0] , this.dots[2] , this.dots[4] , this.dots[6]]);
		},

		setVector:function(){
			var x = this.xpos,
				y = this.ypos,
				z = this.zpos,
                dis = this.w / 2;

			this.dots[0].x = x - dis;
			this.dots[0].y = y - dis;
			this.dots[0].z = z + dis;

			this.dots[1].x = x - dis;
			this.dots[1].y = y + dis;
			this.dots[1].z = z + dis;

			this.dots[2].x = x + dis;
			this.dots[2].y = y - dis;
			this.dots[2].z = z + dis;

			this.dots[3].x = x + dis;
			this.dots[3].y = y + dis;
			this.dots[3].z = z + dis;

			this.dots[4].x = x + dis;
			this.dots[4].y = y - dis;
			this.dots[4].z = z - dis;

			this.dots[5].x = x + dis;
			this.dots[5].y = y + dis;
			this.dots[5].z = z - dis;

			this.dots[6].x = x - dis;
			this.dots[6].y = y - dis;
			this.dots[6].z = z - dis;

			this.dots[7].x = x - dis;
			this.dots[7].y = y + dis;
			this.dots[7].z = z - dis;
		},

        move:function(xc, yc, zc){
            this.xpos += xc;
            this.ypos += yc;
            this.zpos += zc;

            this.dots.forEach(function(dot){
                dot.x += xc;
                dot.y += yc;
                dot.z += zc;
            })
        },

		rotate:function(angleX , angleY){
			var xcos = Math.cos(angleX);
			var xsin = Math.sin(angleX);
			var ycos = Math.cos(angleY);
			var ysin = Math.sin(angleY);

			var x, y, z;
			var nx, ny, nz;
			var xa = 0, ya = 0, za = 0;

			//计算旋转后的所有点坐标位置
			this.dots.forEach(function(dot){
				x = dot.x;
				y = dot.y;
				z = dot.z;

				//绕X轴旋转
				ny = y * xcos - z * xsin;
				nz = z * xcos + y * xsin;

				//绕Y轴旋转
				nx = x * ycos - nz * ysin;
				nz = nz * ycos + x * ysin;

				dot.x = nx;
				dot.y = ny;
				dot.z = nz;

				xa += nx;
				ya += ny;
				za += nz;
			});

			//更新中心点坐标
			this.xpos = xa/this.dots.length;
			this.ypos = ya/this.dots.length;
			this.zpos = za/this.dots.length;
		}
	};

	function drawFace(face){
		if(!face.color){
			face.color = "rgba(" + parseInt(getRandom(128, 255)) + "," + parseInt(getRandom(128, 255)) + "," + parseInt(getRandom(128, 255)) + ",1)";
		}

		ctx.save();
		ctx.beginPath();
		ctx.moveTo(get2d(face[0]).x, get2d(face[0]).y);
		ctx.lineTo(get2d(face[1]).x, get2d(face[1]).y);
		ctx.lineTo(get2d(face[2]).x, get2d(face[2]).y);
		ctx.lineTo(get2d(face[3]).x, get2d(face[3]).y);
		ctx.closePath();
//
//        ctx.strokeStyle = "#fff";
//        ctx.stroke();

		ctx.fillStyle = face.color;
		ctx.fill();
	}

	function get2d(dot){
		var scale = fallLength / (fallLength + dot.z);
		var x = centerX + dot.x * scale;
		var y = centerY + dot.y * scale;
		return {x: x, y: y};
	}

    window.onmousewheel = function(e){
        cubeList.forEach(function(cube){
            var nx = cube.xpos/10;
            var ny = cube.ypos/10;
            var nz = cube.zpos/10;

            nx = e.wheelDelta > 0 ? nx : -nx;
            ny = e.wheelDelta > 0 ? ny : -ny;
            nz = e.wheelDelta > 0 ? nz : -nz;

            cube.move(nx, ny, nz);
        })
    };

	//鼠标事件绑定
	canvas.onmousedown = function(e){
		var angleY,angleX;

		e = e || window.event;
		var x1 = e.clientX - canvas.offsetLeft - centerX;
		var y1 = e.clientY - canvas.offsetTop - centerY;

		var xc = 0, yc = 0;
        var date = new Date();
        var dc = 10000;

		cubeList.forEach(function(cube){
			cube.inertia = false;
		});

		window.onmousemove = function(e){
			e = e || window.event;
			var x2 = e.clientX - canvas.offsetLeft - centerX;
			var y2 = e.clientY - canvas.offsetTop - centerY;
            var date2 = new Date();

			xc = x2 - x1;
			yc = y2 - y1;

			angleY = xc * 0.005;
			angleX = yc * 0.005;

			cubeList.forEach(function(cube){
				cube.rotate(angleX, angleY);
			});

			x1 = x2;
			y1 = y2;
            date = date2;
		};

		window.mouseleave = window.onmouseup = function(e){
			window.onmousemove = null;
            var date2 = new Date();
            var dc = (date2-date)||1;

			cubeList.forEach(function(cube){
				cube.inertia = true;
				cube.toAngle = {
					x : yc/dc,
					y : xc/dc
				};
			});
		}
	};

	/**
	 * 动画初始化
	 */
	var cubeList = [],
		cubeFace = [],
		distance = 200,
        level = 3,
        cube_w = 100;
	function initAnimate(){
        var cw = cube_w/level;
		var dis = cw + distance;
        var minmax = (level - 1) * dis;

        var cube;

		//三层正方体
		for (var l1 = -minmax; l1 <= minmax; l1 += dis) {
			for (var l2 = -minmax; l2 <= minmax; l2 += dis) {
				for (var l3 = -minmax; l3 <= minmax; l3 += dis) {
					cube = new Cube({x: l1, y: l2, z: l3, w: cw});
					cubeFace = cubeFace.concat(cube.faces);
					cubeList.push(cube);

					cube.inertia = true;
					cube.toAngle = {
						x : 2,
						y :	10
					}
				}
			}
		}

		animate();
	}

	function animate(){
		ctx.clearRect(0,0,canvas.width,canvas.height);

		cubeFace.sort(function(a, b){
			var indexa = (a[0].z + a[1].z + a[2].z + a[3].z)/4;
			var indexb = (b[0].z + b[1].z + b[2].z + b[3].z)/4;

			return indexb - indexa;
		});

		//遍历cube判断是否需要旋转
		cubeList.forEach(function(cube){
			if(cube.inertia){
				var ax = cube.toAngle.x * 0.02;
				var ay = cube.toAngle.y * 0.02;

				cube.toAngle.x -= ax;
				cube.toAngle.y -= ay;

				cube.rotate(ax, ay);
			}
		});

		cubeFace.forEach(function(face){
			drawFace(face);
		});

		stats.update();

		RAF(animate);
	}

	function getRandom(a, b) {
		return Math.random() * (b - a) + a;
	}

	initAnimate();
</script>
</body>
</html>